<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
	</body>
</html>
<!DOCTYPE html>
<html lang="en">

	<head>
		<meta charset="UTF-8">
		<meta http-equiv="X-UA-Compatible" content="IE=edge">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title>Document</title>
	</head>

	<body>
		<script>
			const bucket = new WeakMap();
			let activeEffect;
			const effectStack = []; //新增
			//枚举操作类型
			const TriggerType = {
				SET: "SET",
				ADD: "ADD",
				DELETE: "DELETE"
			}
			//原始数据
			const data = {
				foo: {
					bar: "你好啊"
				}
			}
			const ITEATE_KEY = Symbol();
			//定义一个Map实例，存储元素对象到代理对象的映射
			const reactiveMap = new Map();
			//设置里面的函数抽象到’追踪‘
			function track(target, key) {
				if (!activeEffect) {
					return;
				}
				let depsMap = bucket.get(target);
				if (!depsMap) {
					//用对象来做key
					bucket.set(target, (depsMap = new Map()))
				}
				let deps = depsMap.get(key);
				if (!deps) {
					depsMap.set(key, (deps = new Set()));
				}
				deps.add(activeEffect);
			}
			//设置的代码，抽象到这里
			function trigger(target, key, type, newVal) {
				const depsMap = bucket.get(target);
				if (!depsMap) {
					return;
				}

				const effects = depsMap.get(key);

				const iterateEffects = depsMap.get(ITEATE_KEY); //把和ITERATE_KEY关联的副作用取出来
				//新增
				const effectToRun = new Set();
				effects && effects.forEach(fn => {
					if (fn !== activeEffect) {
						effectToRun.add(fn);
					}
				});
				if (type === TriggerType.ADD || type === TriggerType.DELETE) {
					iterateEffects && iterateEffects.forEach(fn => {
						if (fn !== activeEffect) {
							effectToRun.add(fn);
						}
					});
				}
				//针对数组通过索引新增操作
				if (type === TriggerType.ADD && Array.isArray(target)) {
					//取出和length
					let lengthEffects = depsMap.get("length");
					lengthEffects && lengthEffects.forEach(fn => {
						if (fn !== activeEffect) {
							effectToRun.add(fn);
						}
					});
				}
				console.log("key,length", key, newVal)
				//针对length操作
				if (Array.isArray(target) && key === 'length') {
					//遍历所有数组元素的副作用函数
					depsMap.forEach((effects, k) => {
						//这里的k有可能是length
						//如果由修改length，会导致length关联的effect和元素管理的effect的执行
						if (k == 'length' || (k != 'length' && k >= newVal)) {
							effects && effects.forEach(fn => {
								if (fn !== activeEffect) {
									effectToRun.add(fn);
								}
							});
						}
					})

				}

				effectToRun.forEach(fn => {
					//副作用函数，是否存在调度器？？？？？
					console.log("被手机的effect函数是fn：", fn)
					if (fn.options && fn.options.scheduler) {
						fn.options.scheduler(fn)
					} else {
						fn();
					}
				})

			}

			const arrayInstrumentatios = {}
			//从读取方法
			;['includes','indexOf','lastIndexOf'].forEach(method=>{
				const originMethod = Array.prototype[method];
				arrayInstrumentatios[method]=function(...args) {
					//先从代理对象拿去
					let res = originMethod.apply(this, args);
					//如果拿不到就在原始数组上去拿
					if (res === false) {
						res = originMethod.apply(this.raw, args);
					}
					return res;
				}
			})

			function createReactive(obj, isShallow = false, isReadonly = false) {
				return new Proxy(obj, {
					get(target, key, receiver) {
						//非只读才需要建立响应联系
						if (!isReadonly && typeof key !== 'symbol') {
							track(target, key);
						}

						if (key === 'raw') {
							return target;
						}
						//从写部分获取方法
						if (Array.isArray(target) && arrayInstrumentatios.hasOwnProperty(key)) {
							return Reflect.get(arrayInstrumentatios, key, receiver);
						}
						//得到原始结果
						const res = Reflect.get(target, key, receiver);
						//浅操作
						if (isShallow) {
							return res;
						}
						if (typeof res === 'object' && res !== null) {
							//调用reative将结果保障成响应式数据并返回
							return isReadonly ? readonly(res) : reactive(res);
						}
						return res; //返回对应的值
					},
					has(target, key) {
						console.log("触发in 检测", key);
						track(target, key);
						return Reflect.has(target, key);
					},
					ownKeys(target, key) {
						console.log("触发for ... in 检测,是没有第二个参数的，所以是undefined:", key)
						track(target, Array.isArray(target) ? "length" : ITEATE_KEY);
						//track(target,  ITEATE_KEY);
						return Reflect.ownKeys(target);
					},
					deleteProperty(target, key) {
						if (isReadonly) {
							console.warn(`属性${key}是只读`)
							return true;
						}
						const hadKey = Object.prototype.hasOwnProperty.call(target, key);
						const res = Reflect.deleteProperty(target, key);
						if (res && hadKey) {
							trigger(target, key, TriggerType.DELETE);
						}
						return res;
					},
					set(target, key, newVal, receiver) {
						console.log("set", key, newVal)
						if (isReadonly) {
							console.warn(`属性${key}是只读`)
							return true;
						}

						const type = Array.isArray(target) ? (
							Number(key) < target.length ? TriggerType.SET : TriggerType.ADD
						) : (Object.prototype.hasOwnProperty.call(target, key) ? TriggerType.SET :
							TriggerType.ADD)
						let oldVal = target[key];
						const res = Reflect.set(target, key, newVal, receiver);
						//将副作用函数从桶里取出来，并执行
						//只有是被代理对象，才触发
						console.log("target === receiver.raw", target, receiver.raw, type, "key", key, target.length)
						if (target === receiver.raw) {
							//前后值相等，且前后赋值部位null或NaN，才触发effect函数
							if (oldVal !== newVal && (oldVal === oldVal || newVal === newVal)) {
								trigger(target, key, type, newVal);
							}
						}


						return res;
					},

				})
			}
			//深响应
			function reactive(obj) {
				const existionProxy = reactiveMap.get(obj);
				if (existionProxy) {
					return existionProxy;
				}
				const proxy = createReactive(obj);;
				reactiveMap.set(obj, proxy);
				return proxy;
			}
			//浅响应
			function shallowReactive(obj) {
				return createReactive(obj, true);
			}
			//浅只读
			function shallowReadonly(obj) {
				return createReactive(obj, true, true);
			}
			//深只读
			function readonly(obj) {
				return createReactive(obj, false, true);
			}

			function computed(getter) {
				let value;
				let dirty = true;
				const effectFn = effect(getter, {
					lazy: true,
					scheduler() {
						dirty = true;
						trigger(obj, 'value')
					}
				})
				const obj = {
					get value() {
						if (dirty) {
							value = effectFn();
							console.log("计算")
							dirty = false;
						}
						track(obj, 'value')

						return value;
					}
				}
				return obj;
			}
			//用于注册副作用函数
			function effect(fn, options = {}) {
				const effectFn = () => {
					activeEffect = fn;
					//添加到数组最后
					effectStack.push(effectFn); //函数把自己放到数组里面
					const res = fn(); //fn必须有返回值
					//
					effectStack.pop(); //移除掉数组最后一个
					activeEffect = effectStack[effectStack.length - 1];
					return res;
				}
				fn.options = options; //fn才是effect函数
				if (!options.lazy) {
					effectFn();
				}

				return effectFn;
			}

			function traverse(value, seen = new Set()) {
				//如果读取过了，就什么不做，这么核心就是读取，读取会触发收集！！
				if (typeof value !== "object" || value == null || seen.has(value)) {
					return;
				}
				seen.add(value);
				for (const k in value) {
					traverse(value[k], seen);
				}
				return value;
			}

			function watch(source, cb, options = {}) {
				let getter;
				if (typeof source === "function") {
					getter = source;
				} else {
					getter = () => traverse(source);
				}
				let newVal, oldVal;
				let cleanup;

				function onInvalidate(fn) {
					cleanup = fn;
				}

				const job = () => {
					newVal = effectFn();
					if (cleanup) {
						cleanup();
					}
					cb(newVal, oldVal, onInvalidate);
					oldVal = newVal;
				}
				const effectFn = effect(() => getter(), {
					scheduler: job
				})
				if (options.immediate) {
					job();
				} else {
					oldVal = effectFn(); //非立即执行时候，第一次触发watch，oldVal不再是undefined
				}
			}


			//demo
			const obj = {};
			const arr = reactive([obj]);
			console.log(arr.includes(obj))
		</script>
	</body>


</html>
